package com.cyw.common.utils;


import com.cyw.common.domain.dto.DateInfo;
import com.cyw.common.domain.dto.JudgeDate;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.cyw.common.utils.BaseUtils.*;

/**
 * 校验时间相关类
 *
 * @Author:cyw
 * @CreateTime: 2021/3/24 20:02
 **/
public class ValidateDateHelper {

    public static final Integer AVAILABLE = 1;
    public static final Integer UNAVAILABLE = 0;
    public static final Integer WEEK_INDEX = 7;
    public static final Integer NORMAL_SIZE = 8;

    /**
     * 一周、节假日中哪些天可以使用
     * 例如[1,0,1,0,1,1,1,0] 0表示不能使用，1表示可以使用
     * 这里表示周一到周日,最后个为节假日
     */
    private List<Integer> checkWeekDate;

    private static final List<Integer> allLegalWeekList = new ArrayList<>();
    private static final List<Integer> allUnLegalWeekList = new ArrayList<>();

    static {
        for (int i = 0; i < WEEK_INDEX; i++) {
            allLegalWeekList.add(AVAILABLE);
            allUnLegalWeekList.add(UNAVAILABLE);
        }
    }

    /**
     * ------------------------------------ check Start ------------------------------------
     */

    /**
     * 判断 week+节假日 的节假日是否可用
     *
     * @param list week+节假日
     * @return
     */
    public static Boolean availableHoliday(List<Integer> list) {
        return isSame(subHoliday(list), AVAILABLE);
    }

    /**
     * 判断 week+节假日 是否全部可用
     *
     * @param list       week+节假日
     * @param checkLegal 是否需要校验报错
     * @return
     */
    public static Boolean allUnavailable(List<Integer> list, Boolean checkLegal) {
        if (checkLegal != null && checkLegal) {
            checkLegal(list);
        }
        for (Integer l : list) {
            if (isNotSame(l, UNAVAILABLE)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 校验时间区间是否合法 「开始日期必须小于等于结束日期」
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     */
    public static void checkLegal(LocalDate startDate, LocalDate endDate) {
        CheckUtils.checkNull(startDate, "开始日期");
        CheckUtils.checkNull(endDate, "结束日期");

        if (isNotNull(startDate) && isNotNull(endDate)) {
            isThr(startDate.compareTo(endDate) > 0, "开始日期必须小于等于结束日期");
        }
    }

    /**
     * 校验week+节假日是否合法<br/>
     * 1、校验长度是否为8位<br/>
     * 2、校验元素是否合法<br/>
     *
     * @param list
     */
    public static void checkLegal(List<Integer> list) {
        if (isEmpty(list) || isNotSame(NORMAL_SIZE, list.size())) {
            thrBaseException("一周、节假日数组 非法参数");
        }
        checkDay(list);
    }
    /**
     * 校验一周是否合法<br/>
     * 1、校验长度是否为7位<br/>
     * 2、校验元素是否合法<br/>
     *
     * @param list
     */
    public static void checkWeekLegal(List<Integer> list) {
        if (isEmpty(list) || isNotSame(WEEK_INDEX, list.size())) {
            thrBaseException("一周数组 非法参数");
        }
        checkDay(list);
    }

    private static void checkDay(List<Integer> list) {
        for (Integer l : list) {
            if (l < UNAVAILABLE || l > AVAILABLE) {
                thrBaseException("一周数组 非法参数");
            }
        }
    }

    /**
     * ------------------------------------ check End ------------------------------------
     */


    /**
     * ------------------------------------ provider Start ------------------------------------
     */

    /**
     * 根据 开始日期+结束日期 和 一周的可用情况 -> 计算时间区间内可用情况
     *
     * @param startDate     开始日期
     * @param endDate       结束日期
     * @param checkWeekDate 一周的可用情况
     * @return {@link JudgeDate} 可用情况
     */
    public static List<JudgeDate> judgeDailyDate(LocalDate startDate, LocalDate endDate, List<Integer> checkWeekDate) {
        ValidateDateHelper.checkLegal(startDate, endDate);
        ValidateDateHelper.checkLegal(checkWeekDate);

        // 用户勾选的
        List<DateInfo> dates = DateUtils.generateDateInfos(startDate, endDate);
        Map<Integer, List<DateInfo>> map = StreamUtils.groupByProp(dates, DateInfo::getWeekIndex);

        // 返回结果
        List<JudgeDate> result = new ArrayList<>(dates.size());
        // 周一 ~ 周日可用状态
        for (int i = 0; i < checkWeekDate.size() - 1; i++) {
            Integer status = checkWeekDate.get(i);
            List<DateInfo> infos = map.get(i);
            List<JudgeDate> judgeDates = StreamUtils.propList(
                    infos,
                    info -> new JudgeDate(info.getDate(), isSame(ValidateDateHelper.AVAILABLE, status))
            );
            result.addAll(judgeDates);
        }
        return result;
    }

    /**
     * 根据可用的下角标构建 一周 集合
     * @return List
     */
    public static List<Integer> buildWeekList(Integer availableIndex) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < WEEK_INDEX; i++) {
            list.add(availableIndex == i ? AVAILABLE : UNAVAILABLE);
        }
        return list;
    }

    /**
     * 返回 一周 均可用的集合
     *
     * @return List
     */
    public static List<Integer> buildWeekList(Boolean available) {
        return available != null && available ? allLegalWeekList : allUnLegalWeekList;
    }

    /**
     * 返回 一周+节假日 均可用的集合
     *
     * @return List
     */
    public static List<Integer> legalWeekAndHolidayList() {
        List<Integer> list = buildWeekList(true);
        list.add(AVAILABLE);
        return list;
    }

    /**
     * 从 week+节假日 截断出 week集合
     *
     * @param list week+节假日
     * @return
     */
    public static List<Integer> subWeekList(List<Integer> list, Boolean checkLegal) {
        if (checkLegal != null && checkLegal) {
            checkLegal(list);
        }
        return list.subList(0, list.size() - 1);
    }

    /**
     * 获取 集合中 节假日元素
     *
     * @param list
     * @return
     */
    public static Integer subHoliday(List<Integer> list) {
        return list.get(NORMAL_SIZE - 1);
    }

    /**
     * ------------------------------------ provider End ------------------------------------
     */

}